#!/usr/bin/python
# coding: utf-8
from __future__ import unicode_literals
import sys
import os

import ldap

SERVER_URI = "ldap://10.33.64.30:389"
BASE_DN = "dc=VDIN,dc=local"
BIND_DN = "cn=mantis,ou=vCAC,dc=VDIN,dc=local"
BIND_PASSWORD = "123456"
SEARCH_FILTER = "(|(sAMAccountName=%(user)s)(mail=%(user)s))"

ldap.set_option(ldap.OPT_REFERRALS, 0)
con = ldap.initialize(SERVER_URI)
con.simple_bind_s('cn=mantis,ou=vCAC,dc=VDIN,dc=local', '123456')


class LDAPAuthentication(object):

    class AuthenticationFailed(Exception):
        pass

    def __init__(self, username, password):
        self.username = username
        self.password = password
        self.connection = ldap.initialize(SERVER_URI)
        self.connection.set_option(ldap.OPT_REFERRALS, 0)

    def authentication(self):
        user_dn = self.user_dn
        if user_dn is None:
            raise self.AuthenticationFailed("Failed to map the username to a DN")
        try:
            self.connection.simple_bind_s(user_dn.encode('utf-8'), self.password)
        except ldap.INVALID_CREDENTIALS:
            raise self.AuthenticationFailed("User DN/password rejected by LDAP server")
        except Exception as e:
            pass


    @property
    def user_dn(self):
        results = self.search_user()
        if (results is not None) and (len(results) == 1):
            (user_dn, self.user_attrs) = next(iter(results))
        else:
            user_dn = None
        return user_dn

    def search_user(self):
        filterstr = SEARCH_FILTER % {"user": self.username}
        try:
            results = con.search_s(BASE_DN, ldap.SCOPE_SUBTREE, filterstr)
        except ldap.LDAPError as e:
            results = []

        return self._process_results(results)

    def _process_results(self, results):
        results = [r for r in results if r[0] is not None]
        results = _DeepStringCoder('utf-8').decode(results)
        results = [(r[0].lower(), r[1]) for r in results]
        result_dns = [result[0] for result in results]
        return results


class _DeepStringCoder(object):

    def __init__(self, encoding):
        self.encoding = encoding

    def decode(self, value):
        try:
            if isinstance(value, bytes):
                value = value.decode(self.encoding)
            elif isinstance(value, list):
                value = self._decode_list(value)
            elif isinstance(value, tuple):
                value = tuple(self._decode_list(value))
            elif isinstance(value, dict):
                value = self._decode_dict(value)
        except UnicodeDecodeError:
            pass
        
        return value

    def _decode_list(self, value):
        return [self.decode(v) for v in value]

    def _decode_dict(self, value):
        decoded = ldap.cidict.cidict()
        for k, v in value.items():
            decoded[self.decode(k)] = self.decode(v)
        return decoded


if __name__ == '__main__':
    username = os.environ.get("username")
    password = os.environ.get("password")
    try:
        LDAPAuthentication(username, password).authentication()
    except:
        sys.exit(1)
